/**
**************************************************************************************
*Founder Effect                                                                      *
*Copyright (C) 2006-2007, 5d Studios (www.5d-Studios.com)                            *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
#include "StdAfx.h"
#include "EntityManager.h"
#include "IEntity.h"
#include "IWeapon.h"
#include "Player.h"

#include "../shared/StringUtil.h"
#include "../shared/vector.h"
#include "../shared/QuadTree.h"
#include "../render/ViewManager.h"
#include "../kernel.h"

extern Kernel* g_kernel;

#define TIME (g_kernel->GetTime())
#define GRAVITY (20.0f)

/**
 * Send a ADDENTITY Event 
 */
void AddEntityEvent( IEntity* entity )
{
	// Send an event stating we have removed this entity 
	IEvent evt( EVT_ADD_ENTITY, TIME, new EntityAdditionData( entity ), LT_ADD_RM_ENTITY );
	EventManager::GetInstance()->TriggerEvent( evt );
}

/**
 * Send A REMOVEENTITY Event
 */
void RemoveEntityEvent( IEntity* entity )
{
	// Send an event stating we have removed this entity 
	IEvent evt( EVT_RM_ENTITY, TIME, new EntityAdditionData( entity ), LT_ADD_RM_ENTITY );
	EventManager::GetInstance()->TriggerEvent( evt );
}

// id counter
unsigned int EntityManager::m_idcounter = 1;
// our singleton
EntityManager* EntityManager::instance = new EntityManager;
/**
====================================
EntityManager
====================================
*/
EntityManager::EntityManager(void)
{
    m_player = NULL;
    m_scene = NULL;
    m_entityQuadTree = NULL;
}


/**
============================
Get the player
============================
*/
Player* EntityManager::GetPlayer()
{
    // return the player
    return m_player;
}


/**
==================================
Update each entity
==================================
*/
void EntityManager::Update(float deltaTime, ICamera* cam) 
{
    if ( !Scene::IsLoading() && m_scene )
    {
        Vector2f cameraPosition = cam->GetPosition();
        Rect viewport = cam->GetViewPort();
        Rect r( cameraPosition.x, cameraPosition.y, viewport.width, viewport.height );

        // gather entities that are in the viewport
        type_EntityMap entitiesToUpdate;
        m_entityQuadTree->GetObjects( r, entitiesToUpdate );

        // Update all the entities, test collisions
        m_collisionDetection.Update( cam, m_scene, entitiesToUpdate, deltaTime );
    }
    CleanDeadEntities();
}



/** Prune the entity List */
void EntityManager::CleanDeadEntities()
{
    type_EntityList::iterator it = m_entities.begin();
    while( it != m_entities.end() )
    {
        IEntity* ent = (*it);

        // release any free entities
        if ( ent->CanRemove() )
        {
            it = RemoveEntity( ent );
        }
        else
        {
            ++it;
        }
    }
}

/** Remove the entity without deleting it */
void EntityManager::RemoveWithoutDeleting(IEntity* ent)
{
    type_EntityList::iterator it = m_entities.begin();
    for(; it != m_entities.end(); it++)
    {
        // we found the entity
        if ( (*it) == ent) {
            m_entityQuadTree->Pop( ent );
            m_entities.erase( it );
            return;
        }
    }
}


/** Remove the entity entirely */
type_EntityList::iterator EntityManager::RemoveEntity(IEntity* ent)
{
    type_EntityList::iterator it = m_entities.begin();
    for(; it != m_entities.end(); it++)
    {
        IEntity* entity = (*it);
        // we found the entity
        if ( entity == ent ) 
        {
            g_kernel->LPrintf(  ("Trying to Delete: " + ent->GetName() ).c_str() );
            g_kernel->LPrintf(  "Before: %d", m_entityQuadTree->Size() );
            if ( !m_entityQuadTree->Pop( ent ) ) 
            {
                g_kernel->LPrintf(  ("****WARNING::Could not Pop: " + ent->GetName() +" continuing!*****").c_str() );
                // continue;
            }
            g_kernel->LPrintf(  "After: %d", m_entityQuadTree->Size() );
            g_kernel->LPrintf( "--Deleting from EntityManager:: Releasing %d  bytes", sizeof( (*ent) ) );

            RemoveEntityEvent( ent );
            delete entity; 
            return m_entities.erase( it );
        }
    }
    return m_entities.end();
}

/** Deletes all the entities */
void EntityManager::DeleteAllEntities()
{
    //===========================================================================
    //  FIXME: DO NOT delete entities, we call this function when we load a new map,
    //  set the entities to remove, so they safely delete next frame
    //===========================================================================

    // delete each entity
    type_EntityList::iterator it = m_entities.begin();
    for(; it != m_entities.end(); ++it )
    {
        IEntity* ent = (*it);
        if ( ent != m_player )
            (*it)->SetRemove( true );
    }



    /** Clear the tree */
    if ( m_entityQuadTree ) {
        g_kernel->LPrintf( "Size b4: %d", m_entityQuadTree->Size() );
        m_entityQuadTree->Clear();
        g_kernel->LPrintf( "Size after: %d", m_entityQuadTree->Size() );
    }

    // TEMP
    PutInWorld( m_player );
}

/** Find Entity by Name */
IEntity* EntityManager::Find( const std::string &name )
{
    type_EntityList::iterator it = m_entities.begin();
    for(; it != m_entities.end(); it++)
    {
        // we found the entity
        if ( (*it)->GetName() == name )
            return (*it);
    }
    return NULL;
}


/** Find Entity by id */
IEntity* EntityManager::Find( int id )
{
    type_EntityList::iterator it = m_entities.begin();
    for(; it != m_entities.end(); it++)
    {
        // we found the entity
        if ( (*it)->GetID() == id)
            return (*it);
    }
    return NULL;

}

/** 
 * ** Return a list of all entities in a region *
 */
type_EntityMap EntityManager::FindInRegion( const Rect &region )
{
    type_EntityMap list;
    m_entityQuadTree->GetObjects( region, list );

    return list;
}

/** 
    Return a list of all entities that lie on the line
  */
type_EntityMap EntityManager::FindInLine( const Vector2f &start, const Vector2f &end )
{
    type_EntityMap list;
    m_entityQuadTree->GetObjects( start, end, list );
    return list;
}

/** HACK--How to register the Map with entities?
    Reference: Currently I register the scene
    when we load a new Scene, look at InGameStateView::LoadScene()
    with the EntityManager, this is horrible and NEEDS fixing.
  */
void    EntityManager::SetScene(Scene* scene) 
{ 
    if ( m_entityQuadTree ) 
        delete m_entityQuadTree;

    int w = 0, h = 0;    
    m_scene = scene;
    m_scene->TileToWorld( m_scene->GetMaxX(), m_scene->GetMaxY(), w, h );
    
    m_entityQuadTree = new QuadTree<IEntity*>( 64,64, w, h  );
    m_collisionDetection.SetQuadTree( m_entityQuadTree );

    // TEMP
    if ( m_player )
        PutInWorld( m_player );
}
/** Place the Entity */
void EntityManager::PutInWorld( IEntity* ent )
{
    /*
        Place in the m_entityQuadTree
     */
    if ( m_entityQuadTree )
    {
        m_entityQuadTree->Pop( ent );
        m_entityQuadTree->Put( ent );
    }
}

/** Register the entity */
void EntityManager::RegisterEntity( IEntity* ent, const std::string &entName ) 
{ 
    ent->SetName( entName );
    if ( ent->GetType() == TYPE_PLAYER )
    {
        m_player = static_cast<Player*>( ent );
        m_player->SetID( 0 );   // the player has the special id of 0
    }
    else
    {
        ent->SetID ( m_idcounter++ );   // set the id
		// Send an event stating we have added this entity 
		// NOTE: For now, do not add the player
		AddEntityEvent( ent );
    }

    AddEntity( ent );
    PutInWorld( ent );
}

/** Add the entity to the map */
void EntityManager::AddEntity( IEntity* ent) 
{
    type_EntityList::iterator it = m_entities.begin();
    for (; it != m_entities.end(); ++it )
        if ( *it == ent ) return;

    m_entities.push_back( ent );
}

/** Delete the Player */
void EntityManager::DeletePlayer()
{
    RemoveEntity( m_player );
}

EntityManager::~EntityManager(void)
{
    DeleteAllEntities();

    // delete this instance
	if ( instance ) {
		delete instance;
		instance = NULL;
	}
    // delete the player
    DeletePlayer();
  
}
